#coding:utf-8
'''
Created on 2015-8-11

@author: root
'''
from django.core.cache import cache
import random
import json,hashlib,time,urllib2
import xml.etree.ElementTree as ET
import urllib
import requests
import os

APPID = "wx0024ae8878c66ea5"
APPSECRET = "7d476937465ed0d19dc16357d0d8588b" #"b5281d7ce63026bad78aeb9234db24bb"
TOKEN = "vojAE5QPiIp9TBJexWl6k2zh4UFMfmda"
EXPIRES_IN = 7000

#支付相关
MCH_ID = "1332979101"#微信支付分配的商户号
NOTIFY_URL="http://pay.ugolang.com/payback"#支付回调地址
TRADE_TYPE = "JSAPI"
APIKEY = "b1b2603bdd90ab9b47c54df3440489ab"#"支付密钥
#服务域名
DOMAIN = "pay.ugolang.com"

#微信API常用URL
ACCESS_GET_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
PAY_API_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"
PUSH_CUSTOMER_MSG = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
PUSH_TEMPLATE_MSG = "https://api.weixin.qq.com/cgi-bin/message/template/send"
AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"
TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"


#网站域名
WEBURL = 'http://123.tg.ugolang.com'



def singleton(cls, *args, **kw):
    """单例装饰器
    """
    instances = {}  
    def _singleton():  
        if cls not in instances:  
            instances[cls] = cls(*args, **kw)  
        return instances[cls]  
    return _singleton

def get_api_sign(props,appsecret):
    """
    """
    souce_str = wxhelper.dict_to_url(props)
    md = hashlib.md5()
    md.update(souce_str+"&key=%s"%appsecret)
    sign = md.hexdigest().upper()
    return sign

def check_api_sign(props,appsecret):
    """验证请求信息
    """
    _sign = props.pop("sign")
    return _sign==get_api_sign(props, appsecret)

@singleton
class WxHelper(object):
    """微信接口处理
    """
    
    def __init__(self):
        """微信接口处理
        """
        self.cache = cache#缓存，用于缓存access_token和jsapi_ticket
        
    def xml_to_dict(self,xml_body):
        """xml转化成字典
        """
        json_data = {}
        root = ET.fromstring(xml_body)
        for child in root:
            value = child.text
            json_data[child.tag] = value
        return json_data
    
    def dict_to_url(self,props):
        """字典生成url的GET参数链接
        """
        parm_items = sorted(props.items(), key=lambda d: d[0])
        parmlist = ["%s=%s"%item for item in parm_items]
        return "&".join(parmlist)
    
    def dict_to_xml(self,props):
        """字典生成XML格式数据
        """
        parm_items = ["<%s>%s</%s>"%(item[0],item[1],item[0]) for item in props.items()]
        return "<xml>"+"".join(parm_items)+"</xml>"
    
    def get_md5_sign(self,souce_str):
        """获取加密码
        """
        md = hashlib.md5()
        md.update(souce_str+"&key=%s"%APIKEY)
        sign = md.hexdigest().upper()
        return sign
        
    def check_signature(self,signature,timestamp,nonce):
        """微信接入检测
        """
        sourcestrlist =[TOKEN,timestamp,nonce]
        sourcestrlist.sort()
        sourcestr = "".join(sourcestrlist)
        sha = hashlib.sha1(sourcestr)
        return sha.hexdigest().lower()==signature.lower()
        
    def refresh_access_token(self):
        """刷新access_token
        """
        url = ACCESS_GET_URL+"&appid=%s&secret=%s"%(APPID,APPSECRET)
        print 'url',url
        response = urllib2.urlopen(url)
        data = json.loads(response.read())
        access_token = data.get('access_token')
        self.cache.set("access_token",access_token,EXPIRES_IN)
        return access_token
        
    @property
    def access_token(self):
        """获取access_token
        """
        access_token = self.cache.get("access_token")
        if not access_token:
            self.cache.add("access_token","null",1)
            return self.refresh_access_token()
        else:
            return access_token
        
    def refresh_jsapi_ticket(self):
        """刷新access_token
         {
            "errcode":0,
            "errmsg":"ok",
            "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKdvsdshFKA",
            "expires_in":7200
        }
        """
        url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"%self.access_token
        print url
        response = urllib2.urlopen(url)
        data = json.loads(response.read())
        ticket = data.get('ticket')
        print 'ticket',ticket
        cache.set('jsapi_ticket',ticket,EXPIRES_IN)
        return ticket
        
    @property
    def jsapi_ticket(self):
        """获取jsapi_ticket
        """
        jsapi_ticket = self.cache.get("jsapi_ticket")
        if not jsapi_ticket:
            self.cache.add("jsapi_ticket","null",1)
            return self.refresh_jsapi_ticket()
        else:
            return jsapi_ticket
        
    def produce_auth_url(self,url,scope="snsapi_base"):
        """生成用户授权url地址
        """
        redirect_url= urllib2.quote(url)
        props = {"appid":APPID,"redirect_uri":redirect_url,
                 "response_type":"code","scope":scope,"state":"1#wechat_redirect"}
        url_params = self.dict_to_url(props)
        return AUTHORIZE_URL+"?"+url_params
        
    def api_user_info(self,openid):
        """根据api获取用户信息
        """
        url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN"%(self.access_token,openid)
        result = urllib.urlopen(url).read()
        data = json.loads(result)
        if data.get("errcode")==40001:#如果access_token错误
            url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN"%(self.access_token,openid)
            result = urllib.urlopen(url).read()
            data = json.loads(result)
            return data
        return data
        
    def auth2_access_info(self,code):
        """网页获取access_info
        {
           "access_token":"ACCESS_TOKEN",
           "expires_in":7200,
           "refresh_token":"REFRESH_TOKEN",
           "openid":"OPENID",
           "scope":"SCOPE"
        }
        """
        api = """https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"""%(APPID,APPSECRET,code)
        result = urllib2.urlopen(api).read()
        access_info = json.loads(result)
        return access_info
    
    def auth2_openid(self,code):
        """网页获取openid
        """
        access_info = self.auth2_access_info(code)
        return access_info["openid"]
        
    def auth2_user_info(self,code):
        """根据微信浏览器获取用户信息
        {
           "openid":" OPENID",
           " nickname": NICKNAME,
           "sex":"1",
           "province":"PROVINCE"
           "city":"CITY",
           "country":"COUNTRY",
            "headimgurl": "URL", 
            "privilege":["PRIVILEGE1"，"PRIVILEGE2"],
            "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
        }
        """
        access_info = self.auth2_access_info(code)
        openid = access_info.get("openid")
        access_token = access_info.get("access_token")
        url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN"%(access_token,openid)
        result = urllib2.urlopen(url).read()
        return json.loads(result)
    
    def produce_response_msg(self,ToUserName,FromUserName,Content):
        """生成被动回复微信消息
        """
        if not Content:
            return ""
        CreateTime = int(time.time())
        formatXml = """
            <xml>
            <ToUserName><![CDATA[%s]]></ToUserName>
            <FromUserName><![CDATA[%s]]></FromUserName>
            <CreateTime>"%s"</CreateTime>
            <MsgType><![CDATA[text]]></MsgType>
            <Content><![CDATA[%s]]></Content>
            </xml>
            """%(ToUserName,FromUserName,CreateTime,Content)
        return formatXml
    
    def push_wx_message(self,ToUserName,Content):
        """主动推送客服消息
        """
        url = PUSH_CUSTOMER_MSG+"?access_token=%s"%self.access_token
        msg = Content
        data = {"msgtype": "text", "touser": ToUserName, "text": {"content": msg}}
        ss = json.dumps(data,ensure_ascii=False)
        result = urllib2.urlopen(url, str(ss)).read()
        return result
    
    def get_nonce_str(self):
        """生成随机码
        """
        all_char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        return ''.join(random.choice(all_char) for _ in range(16))
    
    def get_sign(self,dict_data):
        """根据字典数据生成加密码
        """
        souce_str = self.dict_to_url(dict_data)
        return self.get_md5_sign(souce_str)
    
    def get_prepay(self,body,attach,out_trade_no,total_fee,openid,notify_url=NOTIFY_URL,trade_type=TRADE_TYPE,limit_pay=False):
        """获取支付package参数
        """
        spbill_create_ip = "127.0.0.1"
        nonce_str = self.get_nonce_str()
        props={"appid":APPID,"mch_id":MCH_ID,"nonce_str":nonce_str,"body":body,
               "attach":attach,"out_trade_no":out_trade_no,"total_fee":total_fee,
               "spbill_create_ip":spbill_create_ip,"notify_url":notify_url,
               "trade_type":trade_type,"openid":openid}
        if limit_pay:
            props['limit_pay']="no_credit"
        sign = self.get_sign(props)
        props['sign']=sign
        xml_parm = str(self.dict_to_xml(props))
        result = urllib2.urlopen(PAY_API_URL, xml_parm)
        response = result.read()
        data = self.xml_to_dict(response)
        print data
        prepay_id = data.get("prepay_id")
        package = {"package":"prepay_id=%s"%prepay_id,
                   "nonceStr":nonce_str,
                   "timeStamp":str(int(time.time())),
                   "appId":APPID,
                   "signType":"MD5"}
        paySign = self.get_sign(package)
        package["paySign"]=paySign
        package["prepay_id"] = prepay_id
        return package
    
    def get_prepay_package(self,prepay_id,nonce_str):
        package = {"package":"prepay_id=%s"%prepay_id,
                   "nonceStr":nonce_str,
                   "timeStamp":str(int(time.time())),
                   "appId":APPID,
                   "signType":"MD5"}
        paySign = self.get_sign(package)
        package["paySign"]=paySign
        package["prepay_id"] = prepay_id
        return package
    
    def check_pay_sign(self,xml_body):
        tmpData = self.xml_to_dict(xml_body)
        sign = tmpData.pop("sign")
        _sign = self.get_sign(tmpData) #本地签名
        if sign == _sign:
            return True
        return False
    
    def get_jssdk_config(self,url):
        """
        """
        ret = {
            'nonceStr': self.get_nonce_str(),
            'jsapi_ticket': self.jsapi_ticket,
            'timestamp': int(time.time()),
            'url': url
        }
        print str(ret)
        string = '&'.join(['%s=%s' % (key.lower(), ret[key]) for key in sorted(ret)])
        ret['signature'] = hashlib.sha1(string).hexdigest()
        ret['appid'] = APPID
        return ret
    
    def transfer_user(self,amount,openid,partber_trade_no,desc):
        """
        """
        ret = {"mch_appid":APPID,"mchid":MCH_ID,"nonce_str":self.get_nonce_str(),
               "partner_trade_no":partber_trade_no,"openid":openid,"check_name":"NO_CHECK",
               "amount":amount,"desc":desc,"spbill_create_ip":"127.0.0.1"}
        string = '&'.join(['%s=%s' % (key.lower(), ret[key]) for key in sorted(ret)])
        ret["sign"] = self.get_md5_sign(string)
        data = self.dict_to_xml(ret)
        cert_pem = ''.join((os.path.dirname(__file__),'/../certs/apiclient_cert.pem'))
        key_pem = ''.join((os.path.dirname(__file__),'/../certs/apiclient_key.pem'))
        result = requests.post(TRANSFER_URL, str(data),cert=(cert_pem,key_pem))
        return self.xml_to_dict(result.text)
    
    def produce_pay_code_1(self,product_id):
        """生成支付扫码(一)
        """
        info = {"appid":APPID,"mch_id":MCH_ID,"time_stamp":int(time.time()),
            "nonce_str":wxhelper.get_nonce_str(),"product_id":"%s"%product_id}
        sign = wxhelper.get_sign(info)
        t_url = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s"%(sign,
                                                        APPID,MCH_ID,product_id,info["time_stamp"],info["nonce_str"])
        return t_url
        
wxhelper = WxHelper()

if __name__ =="__main__":
#     print wxhelper.produce_pay_code_1("15464124634515443")
    dd = wxhelper.transfer_user(1, "oGCeWvx6qsvFASAn8ddDBG8iZ6CU", "123123444",  "TEST")
    print dd
    print dd.get("err_code_des")
    
#weixin://wxpay/bizpayurl?sign=B36F3984AF1CA38B03BEFFA396538B52&appid=wx565f7ef2dca772a2&mch_id=1268274801&product_id=1238475612390&time_stamp=1446429854&nonce_str=uPg0djeOxBsZqIoP

